package com.example.demo.controller;

import com.example.demo.config.RedisConfig;
import io.lettuce.core.support.caching.CacheFrontend;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @program: demo
 * @description:
 * @author: zt22328 赵儒定
 * @create: 2022-04-20 15:42
 **/
@RestController()
public class RedisTestController {

    @Autowired
    RedisTemplate<String, String> redisTemplate;

//    @Autowired
//    CacheFrontend<String, String> cacheFrontend;

//    @GetMapping("/kong/getClientCache")
//    public Map<String,String> getClientCache(){
//        Map<String,String> map = new HashMap<>();
//        map.put("message",cacheFrontend.get("k1"));
//        return map;
//    }
//
//    @GetMapping("/kong/setClientCache")
//    public Map<String,String> setClientCache(){
//        redisTemplate.opsForValue().set("k1", UUID.randomUUID().toString());
//        Map<String,String> map = new HashMap<>();
//        map.put("cacheFrontend",cacheFrontend.get("k1"));
//        map.put("clientCache", RedisConfig.clientCache.get("k1"));
//        return map;
//    }
    AtomicInteger atomicInteger = new AtomicInteger(100);
    BlockingQueue queue = new LinkedBlockingQueue();
    @GetMapping("/kong/localSkill")
    public Map<String,String> localSkill(){
        String userId = UUID.randomUUID().toString();
        long kc = atomicInteger.decrementAndGet();
        if(kc<0){
            atomicInteger.incrementAndGet();
            Map<String,String> map = new HashMap<>();
            map.put("message","已售完");
            return map;
        }
        queue.offer(userId);
        Map<String,String> map = new HashMap<>();
        map.put("message","下单成功");
        return map;
    }

    @GetMapping("/kong/oneRedis")
    public Map<String,String> oneRedis(){
        long kc = redisTemplate.opsForValue().decrement("goods:1001").longValue();
        Map<String,String> map = new HashMap<>();
        map.put("message","下单成功");
        return map;
    }

    @GetMapping("/kong/skill")
    public Map<String,String> skill(){
        String userId = UUID.randomUUID().toString();
        long kc = redisTemplate.opsForValue().decrement("goods:1001").longValue();
        if(kc<0){
            redisTemplate.opsForValue().increment("goods:1001");
            Map<String,String> map = new HashMap<>();
            map.put("message","已售完");
            return map;
        }
        redisTemplate.opsForList().leftPush("user",userId);
        Map<String,String> map = new HashMap<>();
        map.put("message","下单成功");
        return map;
    }

    Map<String,Integer> map1 = new ConcurrentHashMap<>();

    @GetMapping("/kong/lua/skill")
    public Map<String,String> luaSkill(){
//        Integer status = map1.get("goods:1001");
//        if(status!=null){
//            Map<String,String> map = new HashMap<>();
//            map.put("message","已售完");
//            return map;
//        }
        String userId = UUID.randomUUID().toString();
        //调用lua脚本并执行
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(List.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/skill.lua")));
        // 集群环境多key解决方案
        List<Integer> result = redisTemplate.execute(redisScript, Arrays.asList("goods:num:{default}:1","user.{default}.id"), "1",userId);
        if(result==null){
            map1.put("goods:1001",-1);
            Map<String,String> map = new HashMap<>();
            map.put("message","活动已结束");
            return map;
//        }else if(result<=0){
//            map1.put("goods:1001",0);
//            Map<String,String> map = new HashMap<>();
//            map.put("message","已售完");
//            return map;
        }
        Map<String,String> map = new HashMap<>();
        map.put("message","下单成功");
        return map;
    }

    @GetMapping("/kong/skill/init")
    public Map<String,String> skillInit(){
        redisTemplate.opsForValue().set("goods:1001","10");
        redisTemplate.delete("user");
        Map<String,String> map = new HashMap<>();
        map.put("message","ok");
        return map;
    }

    @GetMapping("/kong/getSkill")
    public Map<String,String> getSkill(){
        List<String> list = redisTemplate.opsForList().range("user", 0, 100);
        Map<String,String> map = new HashMap<>();
        map.put("goods",redisTemplate.opsForValue().get("goods:1001"));
        map.put("list",CollectionUtils.isEmpty(list)?"":list.toString());
        return map;
    }

    private static String LUA_SCRIPT_HASH_KEY = "redis:lua:demo";
    private static String LUA_SHA_HASH_KEY = "redis:lua:demo:sha";
//    private static String TOKEN_LIMIT_KEY = "tokenLimit";


    @PostMapping("/test/initLua")
    public LuaValue initLua(LuaValue lv){
        if(lv==null){
            return new LuaValue();
        }
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        hashOperations.put(LUA_SCRIPT_HASH_KEY,lv.key,lv.script);
        String sha = redisTemplate.getConnectionFactory().getConnection().scriptLoad(lv.script.getBytes());
        lv.setSha(sha);
        hashOperations.put(LUA_SHA_HASH_KEY,lv.key,sha);
        return lv;
    }

    @GetMapping("/test/tokenLimit/acquire")
    public Map<String,Object> tokenLimitAcquire(){
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(List.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/token_limit.lua")));
        List<Integer> result = redisTemplate.execute(redisScript, Arrays.asList("tokenLimit"), "acquire","1");
        Map<String,Object> map = new HashMap<>();
        map.put("test",result);
        return map;
    }

    @GetMapping("/test/tokenLimit/init")
    public Map<String,Object> tokenLimitInit(){
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(List.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/token_limit.lua")));
        List<Integer> result = redisTemplate.execute(redisScript, Arrays.asList("tokenLimit"), "init","10","1");
        Map<String,Object> map = new HashMap<>();
        map.put("test",result);
        return map;
    }

    @GetMapping("/test/tokenLimit/del")
    public Map<String,Object> tokenLimitDel(){
        redisTemplate.delete("tokenLimit");
//        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
//        redisScript.setResultType(List.class);
//        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/token_limit.lua")));
//        List<Integer> result = redisTemplate.execute(redisScript, Arrays.asList("tokenLimit"), "init","10","1");
//        Map<String,Object> map = new HashMap<>();
//        map.put("test",result);
        return new HashMap<>(0);
    }



    private static class LuaValue{
        private String key;
        private String sha;
        private String script;

        public LuaValue(){}
        public LuaValue(String key, String sha,String script){
            this.key = key;
            this.sha = sha;
            this.script = script;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getSha() {
            return sha;
        }

        public void setSha(String sha) {
            this.sha = sha;
        }

        public String getScript() {
            return script;
        }

        public void setScript(String script) {
            this.script = script;
        }
    }





}
